/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package continuity;

import continuity.View.NodeView;
import continuity.View.RoomView;
import java.util.ArrayList;
import java.util.List;

/**
 * Az egy helyiségben lévő mezőket tárolja. 
 * A többi helyiséggel való kompatibilitás nyilvántartása is a feladata. 
 * Ha üres a szoba, akkor a nincsenek határmezői.
 * @author FornetTeam
 */
public class Room {
    /**
     * A helyiségben lévő mezők.
     */
    private List<Node> nodes;
    /**
     * A szoba különböző oldalain elhelyezkedő mezők felsorolása. 
     * Egy oldalhoz tartozik egy List<Node>. Indexelésük a Direction osztáy 
     * toInt fgvényenek megfelelően történik (0 - Jobb, 1 - Le, 2 - Bal, 3 - Fel). 
     * A bal és jobb oldali listában fentről lefele vannak benne az mezők, míg 
     * alul és felül balról jobbra.
     * Üres Room esetén az összes borderNode null értékű, viszont a szoba 
     * nagyságának megfelelő null benne van a listákban. 
     * (Ezt a Map swapjénél fogjuk kihasználni).
     */
    private List<List<Node>> borderNodes;
    /**
     * A helyiség, mely más helyiséggel illeszkedik a különböző irányokból. 
     * Az egyes listák egy adott irányból vett kompatibilitást (illeszkedést) 
     * reprezentálnak. Az egyes listák indexelése az Direction osztály toInt 
     * fgvényének megfelelően fog történni, 
     * azaz (0 - Jobb, 1 1 - Le, 2 - Bal, 3 - Fel).
     */
    private List<List<Room>> compatible;
    /**
     * Az adott helyiség szomszédait tárolja 
     * (0 - Jobb, 1 1 - Le, 2 - Bal, 3 - Fel).
     */
    private List<Room> neighbors;
    
    
    /**
     * A konstruktor a leírásnak  (description paraméter) megfelelő szobát hoz létre.
     * @param game A Node-ok létrehozásakor átadandó Communication példány.
     * @param characters A játék karaktereinek listája.
     * @param d A pályán lévő ajtó.
     * @param description  
     */
    public Room(Communication game, ArrayList<Character> characters, ArrayList<String> description, DoorElement d) {

        neighbors=new ArrayList<Room>();
        nodes=new ArrayList<Node>();
        compatible=new ArrayList<List<Room>>();
        borderNodes=new ArrayList<List<Node>>();
        //kompatibilitási listák létrehozása
        //benne van, hogy 4 oldala van a szobának!
        for(int i=0;i<4;i++) {
            compatible.add(new ArrayList<Room>());
            borderNodes.add(new ArrayList<Node>());
            neighbors.add(null);
        }
        //első sor kezelése
        String[] s=description.get(0).split(" ");
        //ha üres szoba
        if(s[0].equals("EmptyRoom")) {
            int height=Integer.parseInt(s[1]);
            int width=Integer.parseInt(s[2]);
            for(int i=0;i<height;i++) {
                borderNodes.get(Direction.Left.toInt()).add(null);
                borderNodes.get(Direction.Right.toInt()).add(null);
            }
            for(int i=0;i<width;i++) {
                borderNodes.get(Direction.Up.toInt()).add(null);
                borderNodes.get(Direction.Down.toInt()).add(null);
            }
        }
        else {
            //nem üres szoba-->leírás alapján létrehozzuk
            int width=s.length;
            int height=description.size();
            for(int i=0;i<description.size();i++) {
                s=description.get(i).split(" ");
                for(int j=0;j<s.length;j++) {
                    Node n=new Node();
                    nodes.add(n);
                    char c=s[j].charAt(0);
                    switch(c) {
                        case 'E':
                            n.setElement(new EmptyElement(game));
                            break;
                        case 'K':
                            KeyElement k =new KeyElement(game);
                            k.setDoor(d);
                            n.setElement(k);
                            break;
                        case 'D':
                            n.setElement(d);
                            break;
                        case 'O':
                            n.setElement(new ObstacleElement(game));
                            break;
                        default:
                            // mi legyen ha érvénytelen a bemenet
                            break;
                    }
                    c=s[j].charAt(1);
                    switch(c) {
                        case '1':
                            n.setIsHere(0, true);
                            characters.get(0).setPosition(n);
                            break;
                        case '2':
                            n.setIsHere(1, true);
                            characters.get(1).setPosition(n);
                            break;
                        case '3':
                            n.setIsHere(0, true);
                            characters.get(0).setPosition(n);
                            n.setIsHere(1, true);
                            characters.get(1).setPosition(n);
                            break;
                    }
                    
                }
            }
            
            //Node-ok szomszédainak beállítása és a borderNodes listák feltöltése
            for(int i=0;i<nodes.size();i++) {
                Node n=nodes.get(i);
                //alsó sor indexei
                if(i>=width*(height-1)) {
                    borderNodes.get(Direction.Down.toInt()).add(n);
                }
                else
                {
                    //alsó szomszéd állítás
                    n.setNeighbor(nodes.get(i+width), Direction.Down.toInt());
                }
                //felső sor indexei
                if(i<width) {
                    borderNodes.get(Direction.Up.toInt()).add(n);
                }
                else
                {
                    //felső szomszéd beállítás
                    n.setNeighbor(nodes.get(i-width), Direction.Up.toInt());
                }
                //bal oldal indexei
                if(i%width==0) {
                    borderNodes.get(Direction.Left.toInt()).add(n);
                }
                else {
                    //bal szomszéd beállítása
                    n.setNeighbor(nodes.get(i-1), Direction.Left.toInt());
                }
                //jobb oldal indexei
                if(i%width==width-1) {
                    borderNodes.get(Direction.Right.toInt()).add(n);
                }
                else {
                    //jobb szomszéd beállítása
                    n.setNeighbor(nodes.get(i+1), Direction.Right.toInt());
                }
            }
            //Kész a megfelelő szoba.
        }
    }
    /**
     * Egy szomszédos szoba beállítása,
     * valamint a megfelelő oldalakon lévő mezők összekötése.
     * Ha a két szoba kompatibilis, akkor A nem null, a position oldalon lévő 
     * border Node-oknak beállítjuk szomszédnak a paraméterként kapott room 
     * ellentétes oldalén lévő borderNode-okat. (a Node-ok setNeighbor 
     * függvényének hívogatásáva) Ha nem kompatibilis, akkor meghívjuk a 
     * changeNeighbor függvényt ugyanezekkel a paraméterekkel.
     * @param room Az új szomszédszoba.
     * @param dir Az irány, ahova a szoba szomszédnak kerül.
     */
    public void setNeighbor(Room room, Direction dir) {       
        //megnézzük, hogy kompatibilis-e
        if(compatible.get(dir.toInt()).contains(room)) {
            //ha igen
            for(int i=0;i<borderNodes.get(dir.toInt()).size();i++) {
                borderNodes.get(dir.toInt()).get(i).setNeighbor(
                        room.getBorderNode(dir.getOpposite(), i), dir.toInt());
            }
            neighbors.set(dir.toInt(), room);
        }
        else {
            //ha nem
            changeNeighbor(room, dir);
        }
    }
    /**
     * Az megfelelő oldalon lévő szomszéd lekérdezése (dir.toInt()).
     * @param dir Az irány.
     * @return A szomszéd szoba.
     */
    public Room getNeighbor(Direction dir) {        
        return neighbors.get(dir.toInt());
    }
    /**
     * Egy adott oldal, nbr-edik mezőjét adja vissza.
     * @param dir Az irány.
     * @param nbr Sorszám.
     * @return A megfelelő oldalon és helyen lévő borderNode.
     */
    public Node getBorderNode(Direction dir, int nbr) {    
        return borderNodes.get(dir.toInt()).get(nbr);
    }
    
    /**
     * A neighbor dir-nek megfelelő elemének(dir.toInt()) a paraméterként 
     * kapott room-ot állítja be. Emellett az adott oldalon lévő mezőknek 
     * null-t állít be szomszédainak.
     * @param room Az új szomszéd szoba.
     * @param dir Az irány.
     */
    public void changeNeighbor(Room room, Direction dir){
        //ha emptyRoom a this
        if(getBorderNode(Direction.Up, 0)==null &&
                    getBorderNode(Direction.Down, 0)==null &&
                    getBorderNode(Direction.Right, 0)==null &&
                    getBorderNode(Direction.Left, 0)==null) {
            neighbors.set(dir.toInt(), room);
            return;
        }
        for(int i=0;i<borderNodes.get(dir.toInt()).size();i++) {
            borderNodes.get(dir.toInt()).get(i).setNeighbor(null, dir.toInt());
        }
        
        neighbors.set(dir.toInt(), room);
    }
    
    /**
     * A megfelelő oldali kompatibilitási listához a room hozzáadása.
     * @param r A kompatibilis Romm
     * @param dir Az irány.
     */
    public void addCompatible(Room r, Direction dir){
        compatible.get(dir.toInt()).add(r);         
     }
    @Override
    public String toString() {
        String eol=System.getProperty("line.separator");
        String ret = "";
        //Ha üres a szoba
        if(borderNodes.get(Direction.Right.toInt()).get(0)==null &&
                borderNodes.get(Direction.Down.toInt()).get(0)==null &&
                borderNodes.get(Direction.Left.toInt()).get(0)==null &&
                borderNodes.get(Direction.Up.toInt()).get(0)==null) {
            return "EmptyRoom" + eol;
        }
        int width=borderNodes.get(Direction.Up.toInt()).size();
        int height=borderNodes.get(Direction.Right.toInt()).size();
        for(int i=0;i<height;i++) {
            for(int j=0;j<width;j++) {
                ret+=nodes.get(i*width+j).toString()+ " ";
            }
            ret+=eol;
        }
        return ret;
    }
    
    public RoomView createRoomView() {
        
        int width=borderNodes.get(Direction.Up.toInt()).size();
        int height=borderNodes.get(Direction.Left.toInt()).size();
        NodeView[][] nodeViews=new NodeView[height][width];
        //ha emptyRoom a this
        if(getBorderNode(Direction.Up, 0)==null &&
                    getBorderNode(Direction.Down, 0)==null &&
                    getBorderNode(Direction.Right, 0)==null &&
                    getBorderNode(Direction.Left, 0)==null) {
            NodeView[][] emptyNodes = new NodeView[borderNodes.get(Direction.Left.toInt()).size()][];
            for (int i = 0; i < emptyNodes.length; i++) {
                emptyNodes[i] = new NodeView[borderNodes.get(Direction.Up.toInt()).size()];
            }
            return new RoomView(emptyNodes);
        }
        for(int i=0; i< height;i++) {
            for(int j=0;j<width;j++) {
                nodeViews[i][j]=nodes.get(i*width+j).createNodeView();
            }
        }
        return new RoomView(nodeViews);
    }
}
